/*******************************************************************************
* Copyright (c) 2008, Jeferson Jose de Miranda                                 *
* UDESC - Universidade do Estado de Santa Catarina                             *
* All rights reserved.                                                         *
*                                                                              *
* Redistribution and use in source and binary forms, with or without           *
* modification, are permitted provided that the following conditions are met:  *
*     * Redistributions of source code must retain the above copyright         *
*       notice, this list of conditions and the following disclaimer.          *
*     * Redistributions in binary form must reproduce the above copyright      *
*       notice, this list of conditions and the following disclaimer in the    *
*       documentation and/or other materials provided with the distribution.   *
*     * Neither the name of the Universidade do Estado de Santa Catarina nor   *
*       the names of its contributors may be used to endorse or promote        *
*       products derived from this software without specific prior written     *
*       permission.                                                            *
*                                                                              *
* THIS SOFTWARE IS PROVIDED BY JEFERSON JOSE DE MIRANDA "AS IS" AND ANY        *
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED    *
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE       *
* DISCLAIMED. IN NO EVENT SHALL JEFERSON JOSE DE MIRANDA BE LIABLE FOR ANY     *
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES   *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; *
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND  *
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT   *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF     *
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.            *
*******************************************************************************/

/**
 * \file ImageDifference.hpp
 *
 * \brief Subtracts color images and place the result in a binary image.
 *
 * \author Jeferson Jose de Miranda
 */

#ifndef FRAMEDIFFERENCE_HPP
#define FRAMEDIFFERENCE_HPP

#include "RGBImage.hpp"
#include "GreyLevelImage.hpp"

#define GRAY   false
#define BINARY true

namespace MoveIn
{
    /**
    * \class FrameDifference
    *
    * \brief Deals with image subtion methods.
    */
    class FrameDifference
    {
        public:

        /**
        * \fn FrameDifference::RGBDifference(RGBImage*,
        *                                    RGBImage*,
        *                                    GreyLevelImage*,
        *                                    bool gray_level,
        *                                    unsigned short int,
        *                                    CvRect)
        *
        * \brief Subtracts source image from reference image and stores the
        * resulting gray level image in result.
        *
        * \param reference
        * The RGB reference image.
        *
        * \param source
        * The RGB image witch will be subted from reference image.
        *
        * \param result
        * The resulting gray level image.
        *
        * \param gray_level
        * GRAY for pixel values varying between 0 to 255 and BINARY for pixel
        * values to be either 0 or 255.
        *
        * \param threshold
        * Values below threshold will be black in the resulting image. Other
        * values will be white.
        *
        * \param ROI
        * Rectangle containing the Region of Interest. If NULL, the whole image
        * will be processed in the subtraction.
        */
        static void RGBDifference(RGBImage *reference,
                                  RGBImage *source,
                                  GreyLevelImage  *result,
                                  bool gray_level = GRAY,
                                  unsigned short int threshold = 30,
                                  CvRect *ROI = NULL);

        /**
        * \fn FrameDifference::nomalizedRGBDifference(RGBImage*,
        *                                             RGBImage*,
        *                                             GreyLevelImage*,
        *                                             bool gray_level,
        *                                             unsigned short int,
        *                                             CvRect)
        *
        * \brief Subtracts source image from reference image and stores the
        * resulting gray level image in result. Converts reference and source
        * images to normalized RGB color space in temporary color images before
        * the subtraction process.
        *
        * \param reference
        * The RGB reference image.
        *
        * \param source
        * The RGB image witch will be subted from reference image.
        *
        * \param result
        * The resulting gray level image.
        *
        * \param gray_level
        * GRAY for pixel values varying between 0 to 255 and BINARY for pixel
        * values to be either 0 or 255.
        *
        * \param threshold
        * Values below threshold will be black in the resulting image. Other
        * values will be white.
        *
        * \param ROI
        * Rectangle containing the Region of Interest. If NULL, the whole image
        * will be processed in the subtraction.
        */
        static void normalizedRGBDifference(RGBImage *reference,
                                            RGBImage *source,
                                            GreyLevelImage  *result,
                                            bool gray_level = GRAY,
                                            unsigned short int threshold = 30,
                                            CvRect *ROI = NULL);

    };

/*******************************************************************************
* MoveIn::FrameDifference::RGBDifference                                       *
*******************************************************************************/

    void FrameDifference::RGBDifference(RGBImage *reference,
                                        RGBImage *source,
                                        GreyLevelImage  *result,
                                        bool gray_level,
                                        unsigned short int threshold,
                                        CvRect *ROI)
    {
        // Check the images for the number of channels.
        if(!Image::isEquivalent(reference, source))
        {
            throw new DifferentType();
        }
        // Check the images for the size.
        if(!Image::isSameSize(reference, result))
        {
            throw new DifferentSize();
        }
        else
        {
            // Create a temporary result image.
            RGBImage temp_result(reference->getWidth(),
                                 reference->getHeight());
            // Set image ROI if it is not NULL.
            if(ROI != NULL)
            {
                try
                {
                    reference->setROI(ROI);
                    source->setROI(ROI);
                    temp_result.setROI(ROI);
                    result->setROI(ROI);
                }
                catch(std::exception *e)
                {
                    throw e;
                }
            }
            // Do the subtraction.
            cvAbsDiff(reference->getIplImage(),
                      source->getIplImage(),
                      temp_result.getIplImage());
            // Convert to gray level.
            cvCvtColor(temp_result.getIplImage(),
                       result->getIplImage(),
                       CV_RGB2GRAY);
            // Convert to binary level if requested.
            if(gray_level == BINARY)
            {
                result->binarization(threshold);
            }
            // Reset ROI.
            if(ROI != NULL)
            {
                reference->resetROI();
                source->resetROI();
                result->resetROI();
            }
        }
    }

/*******************************************************************************
* MoveIn::FrameDifference::RGBDifference                                       *
*******************************************************************************/

    void FrameDifference::normalizedRGBDifference(RGBImage *reference,
                                                  RGBImage *source,
                                                  GreyLevelImage  *result,
                                                  bool gray_level,
                                                  unsigned short int threshold,
                                                  CvRect *ROI)
    {
        // Check the images for the number of channels.
        if(!Image::isEquivalent(reference, source))
        {
            throw new DifferentType();
        }
        // Check the images for the size.
        if(!Image::isSameSize(reference, result))
        {
            throw new DifferentSize();
        }
        else
        {
            // Create a temporary result image.
            RGBImage temp_result(reference->getWidth(),
                                 reference->getHeight());
            // Create temporary images, or else reference and source will
            // be permanently modified.
            RGBImage temp_reference(reference->getWidth(),
                                    reference->getHeight());
            temp_reference.copyImage(reference);
            RGBImage temp_source(source->getWidth(),
                                 source->getHeight());
            temp_source.copyImage(source);
            // Set image ROI if it is not NULL.
            if(ROI != NULL)
            {
                try
                {
                    temp_reference.setROI(ROI);
                    temp_source.setROI(ROI);
                    temp_result.setROI(ROI);
                    result->setROI(ROI);
                }
                catch(std::exception *e)
                {
                    throw e;
                }
            }
            // Normalize images.
            temp_reference.normalize();
            temp_source.normalize();
            // Do the subtraction.
            cvAbsDiff(temp_reference.getIplImage(),
                      temp_source.getIplImage(),
                      temp_result.getIplImage());
            // Convert to gray level.
            cvCvtColor(temp_result.getIplImage(),
                       result->getIplImage(),
                       CV_RGB2GRAY);
            // Convert to binary level if requested.
            if(gray_level == BINARY)
            {
                result->binarization(threshold);
            }
            // Reset ROI.
            if(ROI != NULL)
            {
                result->resetROI();
            }
        }
    }
}

#endif

